﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// Pour en savoir plus sur le modèle d'élément Contrôle utilisateur, consultez la page http://go.microsoft.com/fwlink/?LinkId=234236

namespace BlipSansBlop
{
    public sealed partial class Joystick : UserControl
    {
        public static Joystick Instance { set; get; } 
        // Pour en faire un singleton et acceder au seul joystick via Joystick.Instance

        /// <summary>
        /// Position en X et Y
        /// </summary>
        private static double positionX;
        public static double PositionX
        {
            get { return Joystick.positionX; }
            set { Joystick.positionX = value; }
        }
        public static double RapportPositionX
        {
            get { return Joystick.positionX / DISTANCE_MAX; }
            set { Joystick.positionX = value * DISTANCE_MAX; }
        }
        private static double positionY;
        public static double PositionY
        {
            get { return Joystick.positionY; }
            set { Joystick.positionY = value; }
        }
        public static double RapportPositionY
        {
            get { return Joystick.positionY / DISTANCE_MAX; }
            set { Joystick.positionY = value * DISTANCE_MAX; }
        }
        /// <summary>
        /// Angle calculé via Joystick.CalcAngle();
        /// On peut le récupérer en Degrés ou en Radians selon l'utilisation
        /// </summary>
        private static double angle;
        public static double AngleEnDegre
        {
            get { return MainPage.RadianToDegree(angle); }
            set { Joystick.angle = MainPage.DegreeToRadian(value); }
        }
        public static double AngleEnRadian
        {
            get { return Joystick.angle; }
            set { Joystick.angle = value; }
        }
        /// <summary>
        /// Distance calculée via Joystick.CalcDistance();
        /// On peut récupérer la distance dure ou un pourcentage par rapport à son degré de liberté
        /// </summary>
        private static double distance;
        public static double Distance
        {
            get { return Joystick.distance; }
            set { Joystick.distance = value; }
        }
        public static double RapportDistance
        {
            get { return Joystick.distance / DISTANCE_MAX; }
            set { Joystick.distance = value * DISTANCE_MAX; }
        }

        #region Gestion des touches clavier
        /// <summary>
        /// Un booléen par touche, plusieurs touches pouvant être appuiées simultanément ...
        /// </summary>
        private static Boolean zPressed;
        public static Boolean ZPressed
        {
            get { return Joystick.zPressed; }
            set
            {
                Joystick.zPressed = value;
                Joystick.KeyPressed = (Joystick.ZPressed || Joystick.SPressed || Joystick.QPressed || Joystick.DPressed || BoutonA.OPressed || BoutonB.KPressed);
            }
        }
        private static Boolean sPressed;
        public static Boolean SPressed
        {
            get { return Joystick.sPressed; }
            set
            {
                Joystick.sPressed = value;
                Joystick.KeyPressed = (Joystick.ZPressed || Joystick.SPressed || Joystick.QPressed || Joystick.DPressed || BoutonA.OPressed || BoutonB.KPressed);
            }
        }
        private static Boolean qPressed;
        public static Boolean QPressed
        {
            get { return Joystick.qPressed; }
            set
            {
                Joystick.qPressed = value;
                Joystick.KeyPressed = (Joystick.ZPressed || Joystick.SPressed || Joystick.QPressed || Joystick.DPressed || BoutonA.OPressed || BoutonB.KPressed);
            }
        }
        private static Boolean dPressed;
        public static Boolean DPressed
        {
            get { return Joystick.dPressed; }
            set 
            { 
                Joystick.dPressed = value; 
                Joystick.KeyPressed = (Joystick.ZPressed || Joystick.SPressed || Joystick.QPressed || Joystick.DPressed || BoutonA.OPressed || BoutonB.KPressed); 
            }
        }
        /// <summary>
        /// Un booléen héritant des booléens au dessus.
        /// Lorsqu'une ou plusieurs touches sont appuiées, KeyPressed est vrai,
        /// Si aucune touche n'est appuiée, KeyPressed est faux et lance Joystick.Reset(); 
        /// pour replacer le joystick au centre.
        /// </summary>
        private static Boolean keyPressed;
        public static Boolean KeyPressed
        {
            get { return Joystick.keyPressed; }
            set { 
                Joystick.keyPressed = value; 
                if (!(Joystick.ZPressed || Joystick.SPressed || Joystick.QPressed || Joystick.DPressed)) 
                { 
                    Joystick.Reset(); 
                } 
                if (value) 
                { 
                    Joystick.Instance.Opacity = 0.2; BoutonA.Instance.Opacity = 0.2; BoutonB.Instance.Opacity = 0.2; 
                } 
                else 
                { 
                    Joystick.Instance.Opacity = 0.8; BoutonA.Instance.Opacity = 0.8; BoutonB.Instance.Opacity = 0.8; 
                } 
            }
        }
        #endregion

        /// <summary>
        /// Constante correspondant au rayon de liberté du joystick
        /// </summary>
        public static readonly double DISTANCE_MAX = 30;

        public Joystick()
        {
            this.InitializeComponent();
            Joystick.Instance = this;
            // Initialisation des variables
            Joystick.positionX = Canvas.GetLeft(Joystick.Instance.Stick) - 80;
            Joystick.positionY = Canvas.GetTop(Joystick.Instance.Stick) - 80;
            Joystick.CalcAngle();
            Joystick.CalcDistance();
            // On enlève l'inertie du mouvement de dragdrop
            Joystick.Instance.Stick.ManipulationMode =
                ManipulationModes.TranslateX |
                ManipulationModes.TranslateY;
        }

        /// <summary>
        /// Repositionne le joystick au centre
        /// </summary>
        public static void Reset()
        {
            Joystick.PositionX = 0;
            Joystick.PositionY = 0;
            Joystick.CalcAngle();
            Joystick.CalcDistance();
            Joystick.CanvasSet();
        }

        /// <summary>
        /// Actualise l'affichage du joystick avec ses attributs X et Y
        /// </summary>
        public static void CanvasSet()
        {
            Canvas.SetLeft(Joystick.Instance.Stick, Joystick.PositionX + 80);
            Canvas.SetTop(Joystick.Instance.Stick, Joystick.PositionY + 80);
            DebugControl.Update();
        }

        /// <summary>
        /// Renvoit la distance entre le centre et la position du joystick
        /// </summary>
        /// <returns>Distance</returns>
        public static double CalcDistance()
        {
            return Joystick.Distance = Math.Sqrt(Joystick.PositionX * Joystick.PositionX + Joystick.PositionY * Joystick.PositionY);
        }

        /// <summary>
        /// Renvoit l'angle décrit par le joystick
        /// </summary>
        /// <returns>Angle en radian</returns>
        public static double CalcAngle()
        {
            return Joystick.angle = Math.Atan2(Joystick.PositionY, Joystick.PositionX);
        }

        /// <summary>
        /// Bouge le Joystick de X et Y en fonction de sa position actuelle
        /// Si utilisation du clavier, le mouvement se fait à partir du centre et surpasse le dragndrop
        /// </summary>
        /// <param name="X">Mouvement horizontal</param>
        /// <param name="Y">Mouvement vertical</param>
        public static void Move(double X = 0, double Y = 0)
        {
            // Récupération de la position du joystick
            Joystick.PositionX = Canvas.GetLeft(Joystick.Instance.Stick) - 80;
            Joystick.PositionY = Canvas.GetTop(Joystick.Instance.Stick) - 80;
             
            if (Joystick.KeyPressed)
            {
                // Si utilisation du clavier, position en dure a la distance max correspondant aux touches appuiées
                Joystick.Reset();
                if (Joystick.ZPressed) Joystick.PositionY -= DISTANCE_MAX; // Haut
                if (Joystick.SPressed) Joystick.PositionY += DISTANCE_MAX; // Bas
                if (Joystick.QPressed) Joystick.PositionX -= DISTANCE_MAX; // Gauche
                if (Joystick.DPressed) Joystick.PositionX += DISTANCE_MAX; // Droite
            }
            else
            {
                // Sinon, position par rapport à son point actuel + le mouvement Delta
                Joystick.PositionX += X;
                Joystick.PositionY += Y;
            }

            // Pour ne pas que le joystick ne sorte de sa zone de mouvement définie par DISTANCE_MAX
            // + Mise à jour de la distance
            if (Joystick.CalcDistance() > Joystick.DISTANCE_MAX)
            {
                Joystick.PositionX = Joystick.PositionX / Joystick.Distance * Joystick.DISTANCE_MAX;
                Joystick.PositionY = Joystick.PositionY / Joystick.Distance * Joystick.DISTANCE_MAX;
            }
            // Mise à jour de l'angle
            Joystick.CalcAngle();
            // Mise à jour de l'affichage
            Joystick.CanvasSet();
        }
        
        // Gestion des mouvements tactiles (largement simplifiés, ils tennaient sur 50 lignes au départ ...)

        /// <summary>
        /// Méthode lancée lorsque l'objet reçoit un appui long provoquant un déplacement Delta
        /// </summary>
        /// <param name="sender">Correspond à Joystick.Instance.Stick</param>
        /// <param name="e">Fournit les données pour l'évènement</param>
        private void Ellipse_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e)
        {
            Joystick.Move(e.Delta.Translation.X, e.Delta.Translation.Y);
        }

        /// <summary>
        /// Méthode lancée à la fin d'une manipulation tactile
        /// </summary>
        /// <param name="sender">Correspond à Joystick.Instance.Stick</param>
        /// <param name="e">Fournit les données pour l'évènement</param>
        private void Stick_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e)
        {
            Joystick.Reset();
        }

    }
}
